package com.example.timer.helper;


import com.example.timer.entity.JobInfo;
import com.example.timer.executor.JobExecutor;


import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;

/**
 * @author daiguojun 2019-05-21
 */
public class JobScheduleHelper {
    private static Logger logger = Logger.getLogger(JobScheduleHelper.class.getName());

    private static JobScheduleHelper instance = new JobScheduleHelper();
    public static JobScheduleHelper getInstance(){
        return instance;
    }

    public static final long PRE_READ_MS = 5000;    // pre read

    private Thread scheduleThread;
    private volatile boolean scheduleThreadToStop = false;
    private volatile static Map<Integer, List<Integer>> ringData = new ConcurrentHashMap<>();

    public void start(){

        // schedule thread
        scheduleThread = new Thread(new Runnable() {
            @Override
            public void run() {

                try {
                    TimeUnit.MILLISECONDS.sleep(5000 - System.currentTimeMillis()%1000 );
                } catch (InterruptedException e) {
                    if (!scheduleThreadToStop) {
                        logger.severe(e.getMessage());
                    }
                }
                logger.info(">>>>>>>>> init job admin scheduler success.");

                while (!scheduleThreadToStop) {

                    // Scan Job
                    long start = System.currentTimeMillis();

                    boolean preReadSuc = true;
                    try {
                        // 1、pre read
                        long nowTime = System.currentTimeMillis();
                        // 获取调度类型以及时间  固定速度,corn表达式自定义实现
                        List<JobInfo> scheduleList = JobExecutor.loadScheduleList();
                        if (scheduleList!=null && !scheduleList.isEmpty()) {
                            // 2、push time-ring
                            for (JobInfo jobInfo: scheduleList) {
                                // 判断执行时间有没有到 到了就执行
                                if (nowTime > jobInfo.getTriggerTime() + PRE_READ_MS) {
                                    // 调度过期 立刻执行一次
                                    JobTriggerPoolHelper.trigger(jobInfo.getName());
                                    logger.info(">>>>>>>>>>> xxl-job, schedule push trigger once : jobName = " + jobInfo.getName() );
                                    // 刷新 下次执行时间
                                    jobInfo.freshNextTriggerTime(jobInfo,new Date());
                                } else if(nowTime > jobInfo.getTriggerTime()) {
                                    JobTriggerPoolHelper.trigger(jobInfo.getName());
                                    logger.info(">>>>>>>>>>> xxl-job, schedule push trigger : jobName = " + jobInfo.getName() );
                                    // 刷新 下次执行时间
                                    jobInfo.freshNextTriggerTime(jobInfo,new Date());
                                } else {
                                    continue;
                                }
                            }
                        } else {
                            preReadSuc = false;
                        }
                    } catch (Exception e) {
                        if (!scheduleThreadToStop) {
                            logger.severe(">>>>>>>>>>>JobScheduleHelper#scheduleThread error:{}");
                        }
                    } finally {

                    }
                    long cost = System.currentTimeMillis()-start;

                    // Wait seconds, align second
                    if (cost < 1000) {  // scan-overtime, not wait
                        try {
                            // pre-read period: success > scan each second; fail > skip this period;
                            TimeUnit.MILLISECONDS.sleep((preReadSuc?1000:PRE_READ_MS) - System.currentTimeMillis()%1000);
                        } catch (InterruptedException e) {
                            if (!scheduleThreadToStop) {
                                logger.severe(e.getMessage());
                            }
                        }
                    }

                }

                logger.info(">>>>>>>>>>>JobScheduleHelper#scheduleThread stop");
            }
        });
        scheduleThread.setDaemon(true);
        scheduleThread.setName("JobScheduleHelper#scheduleThread");
        scheduleThread.start();

    }

    public void toStop(){
        // 1、stop schedule
        scheduleThreadToStop = true;
        try {
            TimeUnit.SECONDS.sleep(1);  // wait
        } catch (InterruptedException e) {
            logger.severe(e.getMessage());
        }
        if (scheduleThread.getState() != Thread.State.TERMINATED){
            // interrupt and wait
            scheduleThread.interrupt();
            try {
                scheduleThread.join();
            } catch (InterruptedException e) {
                logger.severe(e.getMessage());
            }
        }

        // if has ring data
        boolean hasRingData = false;
        if (!ringData.isEmpty()) {
            for (int second : ringData.keySet()) {
                List<Integer> tmpData = ringData.get(second);
                if (tmpData!=null && tmpData.size()>0) {
                    hasRingData = true;
                    break;
                }
            }
        }
        if (hasRingData) {
            try {
                TimeUnit.SECONDS.sleep(8);
            } catch (InterruptedException e) {
                logger.severe(e.getMessage());
            }
        }

        logger.info(">>>>>>>>>>> job, JobScheduleHelper stop");
    }
}

